package health

import (
	"bytes"
	"context"
	"log"
	"net/http"
	"sync"
	"time"

	"go-gateworker/internal/config"
	"go-gateworker/internal/models"
)

// Checker 健康检查器
type Checker struct {
	servers        []*models.Server
	client         *http.Client
	checkInterval  time.Duration
	checkTimeout   time.Duration
	retryInterval  time.Duration
	config         *config.HealthCheckConfig
	stopChan       chan struct{}
	metrics        *CheckerMetrics
	recoveryNotify chan *models.Server
	wg             sync.WaitGroup
}

// CheckerMetrics 健康检查指标
type CheckerMetrics struct {
	TotalChecks   int64
	FailedChecks  int64
	LastCheckTime time.Time
	RecoveryCount int64
}

// NewChecker 创建新的健康检查器
func NewChecker(servers []*models.Server, cfg config.HealthCheckConfig) *Checker {
	return &Checker{
		servers:       servers,
		checkInterval: time.Duration(cfg.Interval) * time.Second,
		checkTimeout:  time.Duration(cfg.Timeout) * time.Second,
		retryInterval: time.Duration(cfg.RetryInterval) * time.Second,
		config:        &cfg,
		stopChan:      make(chan struct{}),
		client: &http.Client{
			Timeout: time.Duration(cfg.Timeout) * time.Second,
		},
		metrics:        &CheckerMetrics{},
		recoveryNotify: make(chan *models.Server, len(servers)),
	}
}

// Start 启动健康检查
func (c *Checker) Start() {
	c.wg.Add(1)
	defer c.wg.Done()

	ticker := time.NewTicker(c.checkInterval)
	defer ticker.Stop()

	// 立即执行一次健康检查
	c.checkAll()

	for {
		select {
		case <-ticker.C:
			c.checkAll()
		case <-c.stopChan:
			log.Println("健康检查器已停止")
			return
		}
	}
}

// Stop 停止健康检查
func (c *Checker) Stop() {
	close(c.stopChan)
	c.wg.Wait()
}

// ForceCheck 强制立即执行一次健康检查
func (c *Checker) ForceCheck() {
	go c.checkAll()
}

// GetRecoveryChannel 获取服务恢复通知通道
func (c *Checker) GetRecoveryChannel() <-chan *models.Server {
	return c.recoveryNotify
}

// UpdateServers 更新服务器列表
func (c *Checker) UpdateServers(servers []*models.Server) {
	c.servers = servers
}

// checkAll 检查所有服务器
func (c *Checker) checkAll() {
	var wg sync.WaitGroup
	for _, server := range c.servers {
		wg.Add(1)
		go func(s *models.Server) {
			defer wg.Done()
			c.checkServer(s)
		}(server)
	}
	wg.Wait()
}

// checkServer 检查单个服务器
func (c *Checker) checkServer(server *models.Server) {
	// 使用服务器特定的健康检查配置（如果有），否则使用全局配置
	checkCfg := c.config
	if server.Config.HealthCheck != nil {
		checkCfg = server.Config.HealthCheck
	}

	req, err := c.createHealthCheckRequest(server, checkCfg)
	if err != nil {
		log.Printf("创建健康检查请求失败: %s: %v", server.URL, err)
		return
	}

	start := time.Now()
	resp, err := c.client.Do(req)
	c.metrics.LastCheckTime = time.Now()
	c.metrics.TotalChecks++

	currentStatus := server.GetStatus()

	isHealthy := c.isResponseHealthy(resp, err, checkCfg)

	if !isHealthy {
		if currentStatus == models.StatusHealthy {
			log.Printf("服务器故障: %s: %v", server.URL.String(), err)
		}
		server.MarkFailure()
		c.metrics.FailedChecks++

		// 如果服务器不健康或在黑名单中，安排更频繁的重试检查
		if server.GetStatus() == models.StatusUnhealthy || server.GetStatus() == models.StatusBlacklisted {
			if !server.IsRetrying() {
				server.StartRetry()
				go c.scheduleRetry(server, checkCfg)
			}
		}
	} else {
		if currentStatus != models.StatusHealthy {
			log.Printf("服务器恢复: %s", server.URL.String())
			server.MarkSuccess()
			c.metrics.RecoveryCount++

			// 通知恢复
			select {
			case c.recoveryNotify <- server:
				log.Printf("已发送服务器恢复通知: %s", server.URL.String())
			default:
				log.Printf("恢复通知通道已满: %s", server.URL.String())
			}
		}
	}

	if resp != nil {
		resp.Body.Close()
	}

	log.Printf("健康检查完成: %s, 耗时: %v, 状态: %v",
		server.URL.String(),
		time.Since(start),
		server.GetStatus())
}

// createHealthCheckRequest 创建健康检查请求
func (c *Checker) createHealthCheckRequest(server *models.Server, checkCfg *config.HealthCheckConfig) (*http.Request, error) {
	var body []byte
	if checkCfg.CheckBody != "" {
		body = []byte(checkCfg.CheckBody)
	}

	ctx, cancel := context.WithTimeout(context.Background(), c.checkTimeout)
	defer cancel()

	req, err := http.NewRequestWithContext(ctx,
		checkCfg.CheckMethod,
		server.URL.String()+checkCfg.CheckPath,
		bytes.NewBuffer(body))
	if err != nil {
		return nil, err
	}

	// 添加请求头
	for key, value := range checkCfg.CheckHeaders {
		req.Header.Set(key, value)
	}

	return req, nil
}

// isResponseHealthy 检查响应是否健康
func (c *Checker) isResponseHealthy(resp *http.Response, err error, checkCfg *config.HealthCheckConfig) bool {
	if err != nil {
		return false
	}

	// 检查状态码是否在成功列表中
	for _, code := range checkCfg.SuccessCodes {
		if resp.StatusCode == code {
			return true
		}
	}

	return false
}

// scheduleRetry 调度对不健康或黑名单服务器的重试检查
func (c *Checker) scheduleRetry(server *models.Server, checkCfg *config.HealthCheckConfig) {
	ticker := time.NewTicker(c.retryInterval)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			c.checkServer(server)
			if server.IsHealthy() {
				log.Printf("服务器在重试期间恢复: %s", server.URL.String())
				return
			}
		case <-c.stopChan:
			return
		}
	}
}
